/**
 * 初始化翻页控件对象(ES6) --- ie不兼容,需要使用polyfill等解决兼容性
 * @param options 配置对象
 * ==============options======================
 * @param parentId 父元素id,控件生成的位置(不带#字符)
 * @param totalCount 总数据量
 * @param perCount 每页显示数量
 * @param displayPages 控件显示的总页数
 * @param renderParams 自定义函数的参数
 * @description 参数结构: {dataIndex, searchParams... } dataIndex当前页数据的起始下标(参数名限定为dataIndex),searchParam更多检索条件参数。 {dataIndex： 10, userId : 'us001' ...}
 * @param renderFunc 创建元素的自定义函数(要求同步返回结果)。renderFunc(renderParams)： {...}  => 入参:renderParams | 返回值结构(总数和当前页的数据量): {totalCount : 100, pageCount: 10}
 * @description renderFunc返回值说明: totalCount总数据量,pageCount当前页数据量(pageCount大于0才会正常处理,否则会取消选中并且禁用前后页按钮)
 * ==============注意==============
 * 数据删除后需主动判断当前页是否还有数据,然后选择性调用paginationUtil.moveToPrev();即可回到上一页 或者调用其他函数回到第一页
 */
class PaginationUtil {

	constructor(options) {
		if (!options || typeof options.totalCount != 'number' || (options.perCount != undefined && typeof options.perCount != 'number')) {
			console.warn("参数异常...");
			this.hasInitError = true;
			return this;
		}

		if (options.totalCount < 0 || options.perCount < 0) {
			console.warn("不能为负数...");
			this.hasInitError = true;
			return this;
		}

		if (!options.parentId || !document.getElementById(options.parentId)) {
			console.warn("未指定父元素,生成失败...");
			this.hasInitError = true;
			return this;
		}

		if (options.totalCount == 0) {
			console.warn("没有数据...");
			this.hasInitError = true;
			return this;
		}

		this.parentId = options.parentId;
		this.totalCount = options.totalCount;
		this.perCount = options.perCount || 10;

		// 控件显示的页数
		let displayPages = options.displayPages || 7;
		if (displayPages < 7) {
			// 最少显示7页
			displayPages = 7;
		}
		this.displayPages = displayPages;

		this.calRequiredParam();

		// 初始化默认第一页
		this.startPage = 1;
		// 结束页初始化默认区间最大值
		this.endPage = this.range;
		// 选中页初始化默认第一页
		this.currentPage = 1;
		// 自定义函数
		// 自定义函数需要返回一个{总数据量,当前页数据量}对象用于后续更新
		this.renderFunc = options.renderFunc;
		// 自定义函数至少需要有一个包含选中序列的入参对象
		this.renderParams = options.renderParams;
		
		return this;
	}

	/**
	 * 计算必要的动态参数
	 * 总页数和范围区间
	 */
	calRequiredParam() {
		// 总页数计算
		this.totalPage = parseInt(this.totalCount / this.perCount, 10) + (this.totalCount % this.perCount > 0 ? 1 : 0);
		// 起始页-结束页区间  区间值总是为总页数或者设定控件显示的页数
		this.range = this.totalPage > this.displayPages ? this.displayPages : this.totalPage;
		
		
		// 更新后计算结束页,根据结束页计算起始页
		// 翻页后结束页大于总页数
		if (this.endPage > this.totalPage) {
			this.endPage = this.totalPage;
		}
		
		// 结束页-起始页 小于区间时 -> 认为起始页变成1
		if (this.endPage - this.startPage < this.range - 1) {
			this.startPage = 1;
		} else {
			// 结束页减去区间为初始页
			this.startPage = this.endPage - this.range + 1;
		}
	}

	/**
	 * 初始化控件
	 */
	init() {
		// 初始化出错则直接退出
		if (this.hasInitError) {
			return this;
		}
		// 1页不显示控件
		if (this.totalPage > 1) {
			this.createPagination();
		}
		return this;
	}

	/**
	 * 创建控件
	 * @param startPage 起始页(从1开始)
	 * @param endPage 最终页
	 * @param currentPage 当前选中页
	 * @param parentId 宿主元素id
	 *
	 */
	createPagination() {
		let prevDisabled = this.currentPage == 1 ? 'disabled" ' : '" ';
		let prevStyle = this.currentPage == 1 ? 'style="pointer-events: none"' : '';
		let nextDisabled = this.currentPage == this.endPage ? 'disabled" ' : '" ';
		let nextStyle = this.currentPage == this.endPage ? 'style="pointer-events: none"' : '';

		// 未选中则禁用上下页
		if (this.currentPage < 0 || this.currentPage == undefined) {
			prevDisabled = 'disabled" ';
			prevStyle = 'style="pointer-events: none"';
			nextDisabled = 'disabled" ';
			nextStyle = 'style="pointer-events: none"';
		}

		let ul = $("<ul>", { class: "pagination" });
		let prev_a = $('<a href="#">&laquo;</a>');
		prev_a.on("click", () => this.moveToPrev());
		let prev = $('<li class="pagination-prev ' + prevDisabled + prevStyle + '></li>').append(prev_a);
		let next_a = $('<a href="#">&raquo;</a>');
		next_a.on("click", () => this.moveToNext());
		let next = $('<li class="pagination-next ' + nextDisabled + nextStyle + '></li>').append(next_a);
		ul.append(prev);

		let pages = [];
		for (let i = this.startPage; i <= this.endPage; i++) {
			let a = $('<a href="#">' + i + '</a>');
			// ES6以前
			// 直接使用i绑定会产生问题,实际触发事件时i的值始终是循环最后一次的值
			// click事件触发时函数会向上寻找i的值,对应的就会找到循环最终的i
			// 使用立即执行函数创建一个闭包,将每次循环的i传入后立即执行,这样触发click事件时
			// 函数向上寻找i的值会找到闭包传入的值。
			// 闭包做法会导致内存大量占用
			/*(function(j) {
				a.on("click", function() {
					moveToPage(j, this);
				});
			})(i);*/
			// 使用页面上的值直接进行计算
			// a.data("dataIndex", i - 1);
			// 使用ES6的let,将i限定在for循环的作用域内
			a.on("click", () => this.moveToPage(i));
			let li = $('<li>');
			if (this.currentPage == i) {
				li.addClass("active");
			} else {
				li.removeClass("active");
			}
			pages.push(li.append(a));
		}
		ul.append(pages);
		ul.append(next);
		// 生成控件
		$("#" + this.parentId).empty().append(ul);
	}

	/**
	 * 点击翻页事件
	 * @param page 当前页
	 */
	moveToPage(page) {
		if (page < 1 || page > this.totalPage) {
			return;
		}
		
		if (isNaN(parseInt(page))) {
			this.currentPage = 1;
		} else {
			this.currentPage = page;
		}
		
		// 自定义函数执行,自定义函数中需要有请求数据和页面渲染的逻辑处理
		if (typeof this.renderFunc === 'function') {
			// 自定义函数需要返回一个指定的数据结构
			// 自定义函数需要有一个选中序列的入参
			this.renderParams = this.renderParams ?? {};
			// 下一次请求的数据开始下标---此处已经计算完毕，后台直接使用即可
			this.renderParams.dataIndex = (this.currentPage - 1) * this.perCount;
			let result = this.renderFunc(this.renderParams);
			/*{  
			   totalCount: 100,
			   pageCount: 10,
			}*/
			if (result) {
				if (result.totalCount > 0) {
					this.totalCount = result.totalCount;
					if (result.pageCount <= 0 || result.pageCount == undefined) {
						// 当前页没数据->认为当前页数据被清空了，取消选中并且禁用上下页按钮。
						this.currentPage = -1;
					}
				} else {
					console.warn("没有任何数据...");
					this.clear(); 
					return;
				}
			} else {
				console.warn("返回值结构不正确...");
				return;
			}

			// 重新计算必要参数
			this.calRequiredParam();
			
			// 当前页没数据则回到最后一页
			if (this.currentPage == -1) {
				this.moveToLast();
				return;
			}
			
			// 一页不显示控件
			if (this.totalPage == 1) {
				
				// 检测到没有控件则不再调用自身?
				// 清空控件
				this.clear();
				return;
			}
		}

		// ===================以下纯js端分页的控件处理===============
		// 总页数大于区间时,重置起始结束页位置
		if (this.totalPage > this.range) {
			// 区间中间值,用于保持选中页在正中
			let half = parseInt(this.range / 2, 10);
			// 区间中点
			let mid = parseInt((this.startPage + this.endPage) / 2, 10);
			if (this.currentPage > mid) {
				// 超过中点
				if (this.totalPage > this.endPage) {
					// 当前页加上区间中值未超过最大页数时,保持当前页在正中
					if (this.currentPage + half <= this.totalPage) {
						this.endPage = this.currentPage + half;
						this.startPage = this.currentPage - half;
					} else {
						// 未到达最大页数时自增
						this.startPage++;
						this.endPage++;
					}
				}
			} else {
				// 未超过中点
				if (this.startPage > 1) {
					if (this.currentPage - half >= 1) {
						this.startPage = this.currentPage - half;
						this.endPage = this.currentPage + half;
					} else {
						this.startPage--;
						this.endPage--;
					}
				}
			}
		}
		// 重置控件
		this.createPagination();
	};

	/**
	 * 点击上一页事件
	 */
	moveToPrev() {
		let prev = this.currentPage - 1;
		if (prev >= 1) {
			this.moveToPage(prev);
		}

	}

	/**
	 * 点击下一页事件
	 */
	moveToNext() {
		let next = this.currentPage + 1;
		if (next <= this.totalPage) {
			this.moveToPage(next);
		}
	}
	
	/**
	 * 跳转到最后一页
	 */
	moveToLast() {
		this.moveToPage(this.endPage);
	}
	
	/**
	 * 跳转到第一页
	 */
	moveToFirst() {
		this.moveToPage(this.startPage);
	}
	
	/**
	 * 删除控件
	 */
	clear() {
		$("#" + this.parentId).empty();
	}
	
	/**
	 * 隐藏
	 */
	hide() {
		$("#" + this.parentId).hide();
	}
	
	/**
	 * 设置请求参数
	 */
	setRenderParams(params) {
		this.renderParams = {...this.renderParams, ...params}
	}
	
	/**
	 * 重载
	 */
	reload() {
		$("#" + this.parentId).show();
		this.moveToPage(this.currentPage)
	}
	
	/**
	 * 当前页
	 */
	getCurrentPage() {
		return this.currentPage;
	}
}
